﻿using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using DynamicQuery.SampleData;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;

namespace DynamicQuery.Test
{
    [TestClass]
    public class QueryParserTest
    {
        static ShopContainer ctx = new ShopContainer();
       
        [ClassInitialize]
        public static void InitData(TestContext context )
        {
            if (ctx.Database.Exists())
            {
                ctx.Database.ExecuteSqlCommand("ALTER DATABASE " + ctx.Database.Connection.Database + " SET SINGLE_USER WITH ROLLBACK IMMEDIATE");
                ctx.Database.ExecuteSqlCommand("USE master DROP DATABASE " + ctx.Database.Connection.Database);
            }
            ctx.Database.Create();

            Category category = new Category { Name = "Product", Description = "Top Level" };
            ctx.Categories.Add(category);
            ctx.SaveChanges();

            ctx.Categories.Add(new Category { Name = "Food", Description = "Food", ParentCategory = category });
            ctx.Categories.Add(new Category { Name = "Men's Clothes", Description = "Clothes", ParentCategory = category });
            ctx.Categories.Add(new Category { Name = "Women's Clothes", Description = "Clothes", ParentCategory = category });
            ctx.SaveChanges();

            ctx.Products.Add(new Product { CategoryId = 2, Name = "Brown Rice", Price = 22m, Description = "premium rice" });
            ctx.Products.Add(new Product { CategoryId = 2, Name = "White Rice", Price = 25m, Description = "premium rice" });
            ctx.Products.Add(new Product { CategoryId = 2, Name = "White Rice2", Price =  25m, Description = "premium rice" });
            ctx.Products.Add(new Product { CategoryId = 2, Name = "White Rice3", Price =  25m, Description = "premium rice" });
            ctx.Products.Add(new Product { CategoryId = 2, Name = "White Rice4", Price =  25m, Description = "premium rice" });
            ctx.Products.Add(new Product { CategoryId = 2, Name = "White Rice5", Price =  25m, Description = "premium rice" });
            ctx.Products.Add(new Product { CategoryId = 2, Name = "White Rice6", Price =  25m, Description = "premium rice" });
            ctx.Products.Add(new Product { CategoryId = 2, Name = "White Rice7", Price =  25m, Description = "premium rice" });
            ctx.Products.Add(new Product { CategoryId = 2, Name = "White Rice8", Price =  25m, Description = "premium rice" });
            ctx.Products.Add(new Product { CategoryId = 2, Name = "White Rice9", Price = 20m, Description = "premium rice" });

            ctx.Products.Add(new Product { CategoryId = 3, Name = "Levis Jean 501", Price = 29m, Description = "premium jean a" });
            ctx.Products.Add(new Product { CategoryId = 3, Name = "Levis Jean 502", Price = 44m, Description = "premium jean b" });
            ctx.Products.Add(new Product { CategoryId = 3, Name = "Levis Jean 503", Price = 44m, Description = "premium jean c " });
            ctx.Products.Add(new Product { CategoryId = 3, Name = "Levis Jean 504", Price = 44m, Description = "premium jean a" });
            ctx.Products.Add(new Product { CategoryId = 3, Name = "Levis Jean 505", Price = 66m, Description = "premium jean b " });
            ctx.Products.Add(new Product { CategoryId = 3, Name = "Levis Jean 506", Price = 33m, Description = "premium jean C" });

            ctx.Products.Add(new Product { CategoryId = 4, Name = "Levis Jean 501", Price = 29m, Description = "premium jean a" });
            ctx.Products.Add(new Product { CategoryId = 4, Name = "Levis Jean 502", Price = 44m, Description = "premium jean b" });
            ctx.Products.Add(new Product { CategoryId = 4, Name = "Levis Jean 503", Price = 44m, Description = "premium jean b" });
            ctx.Products.Add(new Product { CategoryId = 4, Name = "Levis Jean 504", Price = 44m, Description = "premium jean A" });
            ctx.Products.Add(new Product { CategoryId = 4, Name = "Levis Jean 505", Price = 66m, Description = "premium jean C" });
            ctx.Products.Add(new Product { CategoryId = 4, Name = "Levis Jean 506", Price = 33m, Description = "premium jean B" });

            ctx.SaveChanges();
        }
        
        

        [TestMethod]
        public void TestOnlyOrderByAsc()
        { 
            QueryDescriptor descriptor = new QueryDescriptor
            {
                OrderBy = new OrderByClause { Key = "Name", Order = OrderSequence.ASC }
            };
            var result = ctx.Products.Query(descriptor);

            var list = result.ToList();
            Assert.AreEqual(22, list.Count());
            Assert.AreEqual("Brown Rice", list.First().Name);
            Assert.AreEqual("White Rice9", list.Last().Name);
        }

        [TestMethod]
        public void TestOnlyOrderByDesc()
        {
            QueryDescriptor descriptor = new QueryDescriptor
            {
                OrderBy = new OrderByClause { Key = "Name", Order = OrderSequence.DESC }
            };
            var result = ctx.Products.Query(descriptor);
           
            var list = result.ToList();
            Assert.AreEqual(22, list.Count());
            Assert.AreEqual("Brown Rice",list.Last().Name);
            Assert.AreEqual("White Rice9",list.First().Name);
        }

        [TestMethod]
        public void TestSingleFilterAndNavigationProperty()
        {
            QueryDescriptor descriptor = new QueryDescriptor
            {
                Conditions = new QueryCondition[] { new QueryCondition { Key = "Name",
                    Value = "Rice", 
                    Operator = QueryOperator.CONTAINS }}
            };

            var list = ctx.Products.Query(descriptor).Include(p=>p.Category).ToList();
            Assert.AreEqual(10, list.Count());
            Assert.AreEqual("Food", list.First().Category.Name);
        }

        [TestMethod]
        public void TestMultipleFilterConditions()
        {
            QueryDescriptor descriptor = new QueryDescriptor
            {
                Conditions = new QueryCondition[] {
                    new QueryCondition { Key = "Name",Value = "Rice", Operator = QueryOperator.CONTAINS },
                    new QueryCondition { Key = "Price",Value =  25m, Operator = QueryOperator.LESS },
                    new QueryCondition { Key = "Price",Value = 22m, Operator = QueryOperator.GREATEROREQUAL },
                }
            };

            var result = ctx.Products.Query(descriptor);
            Assert.AreEqual(1, result.Count());
            Assert.AreEqual("Brown Rice", result.First().Name);
        }

        [TestMethod]
        public void TestPagingOnly1()
        {
            QueryDescriptor descriptor = new QueryDescriptor
            {
                OrderBy = new OrderByClause { Key = "Name", Order = OrderSequence.DESC },
                PageSize=5,
                PageIndex=5
            };
            var result = ctx.Products.Query(descriptor);
            Assert.AreEqual(2,result.Count());
            Assert.AreEqual("Brown Rice", result.ToList().Last().Name);
        }

        [TestMethod]
        public void TestPagingOnly2()
        {
            QueryDescriptor descriptor = new QueryDescriptor
            {
                OrderBy = new OrderByClause { Key = "Name", Order = OrderSequence.ASC },
                PageSize = 100,
                PageIndex=1                
            };
            int pageCount;
            var result = ctx.Products.Query(descriptor,out pageCount);
            Assert.AreEqual(1, pageCount);
            Assert.AreEqual(22, result.Count());
            Assert.AreEqual("Brown Rice", result.First().Name);
        }

        [TestMethod]
        public void TestAll()
        {
            QueryDescriptor descriptor = new QueryDescriptor
            {
                OrderBy = new OrderByClause { Key = "Price", Order = OrderSequence.ASC },
                PageSize = 3,
                PageIndex = 1,
                Conditions=new QueryCondition[] {
                    new QueryCondition { Key = "Name",Value = "Rice", Operator = QueryOperator.CONTAINS }
                }
            };

            int pageCount;
            ctx.Products.Query(descriptor, out pageCount);
            Assert.AreEqual(4, pageCount);
        }
    }
}
